package neptune.web.server.services.experiment;

import java.util.Map;
import it.robertobifulco.ias.IAS;
import it.robertobifulco.ias.IncorrectParamsException;
import it.robertobifulco.ias.NotDefinedOperationException;
import it.robertobifulco.ias.Operation;
import it.robertobifulco.ias.UnauthorizedOperationException;
import it.robertobifulco.ias.principalmanager.Principal;
import it.robertobifulco.ias.rolemanager.RoleUndefinedException;
import javax.servlet.http.HttpSession;
import neptune.manager.NeptuneManager;
import neptune.manager.model.experiment.Experiment;
import neptune.manager.model.topology.Topology;
import neptune.manager.storage.ExperimentRepositoryException;
import neptune.manager.validator.ValidatorResult;
import neptune.web.client.model.DefaultRoles;
import neptune.web.client.model.ExperimentInfos;
import neptune.web.client.model.SerializableExperiment;
import neptune.web.client.model.topology.SerializableTopology;
import neptune.web.client.services.SecurityException;
import neptune.web.client.services.experiment.ExperimentCreationException;
import neptune.web.client.services.experiment.ExperimentService;
import neptune.web.client.services.experiment.TopologyConfigurationException;
import neptune.web.server.ApplicationManager;
import neptune.web.server.Debug;
import neptune.web.server.InitializationException;
import neptune.web.server.operations.AddUserToExperiment;
import neptune.web.server.operations.AllocateExperimentWithLK;
import neptune.web.server.operations.AllocateExperimentWithLKShared;
import neptune.web.server.operations.DeleteExperiment;
import neptune.web.server.operations.ExperimentCreation;
import neptune.web.server.operations.GetAllocatedNodes;
import neptune.web.server.operations.GetExperiment;
import neptune.web.server.operations.GetXMLTopology;
import neptune.web.server.operations.RemoveUserFromExperiment;
import neptune.web.server.operations.RequestExperimentStart;
import neptune.web.server.operations.StartExperiment;
import neptune.web.server.operations.StopExperiment;
import neptune.web.server.operations.TerminateExperiment;
import neptune.web.server.operations.TopologyConfiguration;
import neptune.web.server.operations.ValidateTopology;
import neptune.web.server.operations.WriteTopologyFile;
import neptune.web.server.security.IASCreationException;
import neptune.web.server.security.SecurityHelper;
import neptune.web.server.services.SerializationHelper;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * 
 * @author Roberto Bifulco [info@RobertoBifulco.it] [RobertoBifulco.it]
 * @author Luigi Lorenzo Arpino [luigiarpino@yahoo.it]
 * 
 */
public class ExperimentServiceImpl extends RemoteServiceServlet implements
		ExperimentService {

	/**
	 * 
	 */
	private static final long serialVersionUID = 5750368262462337952L;

	/**
	 * Aggiunge un utente ad un esperimento
	 */
	public boolean addUserToExperiment(String username, String expId,
			boolean isSuperUser) throws SecurityException, Exception {
		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			AddUserToExperiment operation = (AddUserToExperiment) SecurityHelper
					.getOperation(AddUserToExperiment.ID, expId, session);

			operation.execute(new Object[] { username, expId,
					new Boolean(isSuperUser) });

			return true;

		} catch (UnauthorizedOperationException e) {
			throw new SecurityException(e.getMessage());
		} catch (RoleUndefinedException e) {
			throw new SecurityException(e.getMessage());
		} catch (IncorrectParamsException e) {
			Debug.printTrace(e);
			throw new Exception(e.getMessage());
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e.getMessage());
		}
	}

	public String allocateExperiment(String expId) throws SecurityException,
			Exception {

		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			Operation op = SecurityHelper.getOperation(StartExperiment.ID,
					session);
			op.execute(new Object[] { expId });
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e);
		}

		return ApplicationManager.getApplicationManager().getNeptuneManager()
				.getExperiment(expId).getState().getValue();
	}

	public String allocateExperiment(String expId,
			Map<String, String[]> allocationMap) throws SecurityException,
			Exception {
		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			Operation op = SecurityHelper.getOperation(StartExperiment.ID,
					session);
			op.execute(new Object[] { expId, allocationMap });
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e);
		}

		return ApplicationManager.getApplicationManager().getNeptuneManager()
				.getExperiment(expId).getState().getValue();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seeneptune.web.client.services.experiment.ExperimentService#
	 * allocateExperimentWithLinKernighan(java.lang.String, int, int)
	 */
	public String allocateExperimentWithLinKernighan(String expId,
			int maxVMsPerNode, int maxBandwidth) throws SecurityException,
			Exception {
		String result = "";
		try {
			Integer integerMaxVMsPerNode = new Integer(maxVMsPerNode);
			Integer integerMaxBandwidth = new Integer(maxBandwidth);
			HttpSession session = getThreadLocalRequest().getSession(false);
			Operation op = SecurityHelper.getOperation(
					AllocateExperimentWithLK.ID, session);
			result = (String) op.execute(new Object[] { expId,
					integerMaxVMsPerNode, integerMaxBandwidth })[0];
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e);
		}
		return result;
		// return
		// ApplicationManager.getApplicationManager().getNeptuneManager().getExperiment(expId).getState().getValue();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seeneptune.web.client.services.experiment.ExperimentService#
	 * allocateExperimentWithLinKernighanSharing(java.lang.String, int, int)
	 */
	public String allocateExperimentWithLinKernighanSharing(String expId,
			int maxVMsPerNode, int maxBandwidth) throws SecurityException,
			Exception {
		String result = "";
		try {
			Integer integerMaxVMsPerNode = new Integer(maxVMsPerNode);
			Integer integerMaxBandwidth = new Integer(maxBandwidth);
			HttpSession session = getThreadLocalRequest().getSession(false);
			Operation op = SecurityHelper.getOperation(
					AllocateExperimentWithLKShared.ID, session);
			result = (String) op.execute(new Object[] { expId,
					integerMaxVMsPerNode, integerMaxBandwidth })[0];
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e);
		}
		return result;
	}

	public String allocationRequest(String expId) throws SecurityException,
			Exception {
		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			Operation op = SecurityHelper.getOperation(
					RequestExperimentStart.ID, expId, session);
			op.execute(new Object[] { expId });
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e);
		}

		return ApplicationManager.getApplicationManager().getNeptuneManager()
				.getExperiment(expId).getState().getValue();
	}

	public boolean createExperiment(String expName)
			throws ExperimentCreationException, SecurityException {
		if (!isExperimentNameValid(expName)) {
			String msg = "\n The experiment's name contains invalid char!";
			throw new SecurityException(msg);
		}
		try {
			IAS ias = ApplicationManager.getApplicationManager().getIAS();

			// Recupera, se presente, la sessione per l'utente
			HttpSession session = getThreadLocalRequest().getSession(false);
			// Verifica la correttezza della sessione
			Principal principal = SecurityHelper.getPrincipal(session);

			ExperimentCreation operation = (ExperimentCreation) ias
					.getOperation(ExperimentCreation.ID, principal);

			operation.execute(new Object[] { principal.getId(), expName });

			return true;

		} catch (IASCreationException e) {
			e.printStackTrace();
			throw new SecurityException(e.getMessage());
		} catch (UnauthorizedOperationException e) {
			e.printStackTrace();
			throw new SecurityException(e.getMessage());
		} catch (RoleUndefinedException e) {
			e.printStackTrace();
			throw new SecurityException(e.getMessage());
		} catch (IncorrectParamsException e) {
			e.printStackTrace();
			throw new ExperimentCreationException(e.getMessage());
		} catch (Exception e) {
			e.printStackTrace();
			throw new ExperimentCreationException(e.getMessage());
		}
	}

	public String deallocateExperiment(String expId) throws SecurityException,
			Exception {
		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			Operation op = SecurityHelper.getOperation(StopExperiment.ID,
					expId, session);
			op.execute(new Object[] { expId });
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e);
		}

		return ApplicationManager.getApplicationManager().getNeptuneManager()
				.getExperiment(expId).getState().getValue();
	}

	public boolean deleteExperiment(String expId) throws SecurityException,
			Exception {

		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			DeleteExperiment operation = (DeleteExperiment) SecurityHelper
					.getOperation(DeleteExperiment.ID, expId, session);

			Boolean isRemoved = (Boolean) operation
					.execute(new Object[] { expId })[0];

			return isRemoved.booleanValue();

		} catch (UnauthorizedOperationException e) {
			throw new SecurityException(e.getMessage());
		} catch (RoleUndefinedException e) {
			throw new SecurityException(e.getMessage());
		} catch (IncorrectParamsException e) {
			Debug.printTrace(e);
			throw new Exception(e.getMessage());
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e.getMessage());
		}

	}

	public String forceExperimentConfiguration(String expId)
			throws SecurityException, Exception {

		// TODO controllo sui cookie
		try {
			// Recupera, se presente, la sessione per l'utente
			HttpSession session = getThreadLocalRequest().getSession(false);
			Operation op = SecurityHelper.getOperation(
					TopologyConfiguration.ID, session);
			op.execute(new Object[] { expId });

			return ApplicationManager.getApplicationManager()
					.getNeptuneManager().getExperiment(expId).getState()
					.getValue();

		} catch (UnauthorizedOperationException e) {
			throw new SecurityException(e.getMessage());
		} catch (RoleUndefinedException e) {
			throw new SecurityException(e.getMessage());
		} catch (NotDefinedOperationException e) {
			throw new SecurityException(e.getMessage());
		} catch (IncorrectParamsException e) {
			Debug.printTrace(e);
			throw new TopologyConfigurationException(e.getMessage());
		} catch (SecurityException e) {
			throw e;
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new TopologyConfigurationException(e.getMessage());
		}
	}

	/*
	 * esempio di stringa restituista:
	 * 
	 * [i] = {"Node1", "RUNNING", "10.0.0.1"}
	 * 
	 * (non-Javadoc)
	 * 
	 * @see
	 * neptune.web.client.services.experiment.ExperimentService#getAllocatedNodes
	 * (java.lang.String)
	 */
	public String[][] getAllocatedNodes(String expId) throws SecurityException,
			Exception {
		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			Operation op = SecurityHelper.getOperation(GetAllocatedNodes.ID,
					expId, session);

			return (String[][]) op.execute(new Object[] { expId })[0];
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e);
		}

	}

	public SerializableExperiment getExperiment(String expId)
			throws SecurityException, Exception {

		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			GetExperiment operation = (GetExperiment) SecurityHelper
					.getOperation(GetExperiment.ID, expId, session);

			Experiment exp = (Experiment) operation
					.execute(new Object[] { expId })[0];

			SerializableExperiment serExp = SerializationHelper
					.toSerializableExperiment(exp);

			return serExp;
		} catch (UnauthorizedOperationException e) {
			throw new SecurityException(e.getMessage());
		} catch (RoleUndefinedException e) {
			throw new SecurityException(e.getMessage());
		} catch (IncorrectParamsException e) {
			Debug.printTrace(e);
			throw new Exception(e.getMessage());
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e.getMessage());
		}
	}

	public String[][] getExperimentsTableData(int begin, int end, int sortId,
			boolean ascending) throws SecurityException, Exception {
		try {
			/*
			 * Non e' necessario il passaggio dall'IAS poiche' il metodo per
			 * recuperare gli esperimenti include la necessita' di passare lo
			 * username, e a livello di logica di business, si occupa di
			 * recuperare i soli dati di interesse per lo username passato
			 */

			HttpSession session = getThreadLocalRequest().getSession(false);
			Principal principal = SecurityHelper.getPrincipal(session);

			boolean isAdmin = false;
			if (principal.getRole().equals(DefaultRoles.SYSTEM_ADMIN))
				isAdmin = true;

			String[][] exps = ApplicationManager.getApplicationManager()
					.getNeptuneManager().getExperimentsRepository()
					.getExperimentTable(begin, end, sortId, ascending,
							principal.getId(), isAdmin);
			return exps;
		} catch (ExperimentRepositoryException e) {
			Debug.printTrace(e);
			throw new Exception(e.getMessage());
		} catch (InitializationException e) {
			Debug.printTrace(e);
			throw new Exception(e.getMessage());
		}
	}

	public String getNodeState(String expId, String nodeId)
			throws SecurityException, Exception {
		// TODO Auto-generated method stub
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seeneptune.web.client.services.experiment.ExperimentService#
	 * getXMLTopologyOfExperiment(java.lang.String)
	 */
	public String getXMLTopologyOfExperiment(String idExperiment)
			throws SecurityException, Exception {
		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			// verifico autorizzazione
			Operation operation = SecurityHelper.getOperation(
					GetXMLTopology.ID, session);
			Object[] obj = operation.execute(new Object[] { idExperiment });
			return (String) obj[0];
		} catch (UnauthorizedOperationException e) {
			throw new SecurityException(e.getMessage());
		} catch (RoleUndefinedException e) {
			throw new SecurityException(e.getMessage());
		} catch (IncorrectParamsException e) {
			Debug.printTrace(e);
			throw new Exception(e.getMessage());
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e.getMessage());
		}
	}

	public boolean isExperimentNameValid(String expName) {
		/*
		 * Codici ASCII A=65 Z=90 a=97 z=122 0=48 9=57 _=95
		 */
		boolean isValid = true;
		byte[] bytes = expName.getBytes();
		for (int i = 0; i < bytes.length; i++) {
			byte b = bytes[i];
			// System.out.println(expName.charAt(i)+"="+b);
			if ((b >= 65 && b <= 90) || (b >= 97 && b <= 122)
					|| (b >= 48 && b <= 57) || (b == 95)) {
				// il carattere è OK
			} else {
				// il carattere non è ammesso
				isValid = false;
			}
		}
		return isValid;
	}

	public boolean removeUserFromExperiment(String username, String expId)
			throws SecurityException, Exception {
		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			RemoveUserFromExperiment operation = (RemoveUserFromExperiment) SecurityHelper
					.getOperation(RemoveUserFromExperiment.ID, expId, session);

			operation.execute(new Object[] { username, expId });

			return true;
		} catch (UnauthorizedOperationException e) {
			throw new SecurityException(e.getMessage());
		} catch (RoleUndefinedException e) {
			throw new SecurityException(e.getMessage());
		} catch (IncorrectParamsException e) {
			Debug.printTrace(e);
			throw new Exception(e.getMessage());
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e.getMessage());
		}
	}

	public String terminateExperiment(String expId) throws SecurityException,
			Exception {
		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			Operation op = SecurityHelper.getOperation(TerminateExperiment.ID,
					expId, session);
			op.execute(new Object[] { expId });
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e);
		}

		return ApplicationManager.getApplicationManager().getNeptuneManager()
				.getExperiment(expId).getState().getValue();
	}

	public ExperimentInfos uploadExperimentTopology(String expId,
			SerializableTopology topology) throws SecurityException, Exception {

		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			WriteTopologyFile operation = (WriteTopologyFile) SecurityHelper
					.getOperation(WriteTopologyFile.ID, expId, session);

			Topology unserTopology = SerializationHelper.toTopology(topology);

			Object[] args = new Object[] { unserTopology, expId };

			ValidatorResult validationResult = (ValidatorResult) operation
					.execute(args)[0];

			ExperimentInfos infos = new ExperimentInfos();

			NeptuneManager manager = ApplicationManager.getApplicationManager()
					.getNeptuneManager();

			infos.experimentStatus = manager.getExperiment(expId).getState()
					.getValue();
			infos.topologyStatus = validationResult.getStatus().toString();
			infos.topologyLog = validationResult.getLog().prettyPrint(0);

			return infos;
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e);
		}
	}

	public ExperimentInfos uploadExperimentXMLTopology(String expId,
			String xmlTopology) throws SecurityException, Exception {
		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			WriteTopologyFile operation = (WriteTopologyFile) SecurityHelper
					.getOperation(WriteTopologyFile.ID, expId, session);

			Object[] args = new Object[] { xmlTopology, expId };

			ValidatorResult validationResult = (ValidatorResult) operation
					.execute(args)[0];

			ExperimentInfos infos = new ExperimentInfos();

			NeptuneManager manager = ApplicationManager.getApplicationManager()
					.getNeptuneManager();
			infos.experimentStatus = manager.getExperiment(expId).getState()
					.getValue();
			infos.topologyStatus = validationResult.getStatus().toString();
			infos.topologyLog = validationResult.getLog().prettyPrint(0);

			return infos;
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e);
		}
	}

	public ExperimentInfos validateExperimentTopology(String expId)
			throws SecurityException, Exception {

		try {
			HttpSession session = getThreadLocalRequest().getSession(false);
			Operation op = SecurityHelper.getOperation(ValidateTopology.ID,
					expId, session);
			ValidatorResult result = (ValidatorResult) op
					.execute(new Object[] { expId })[0];

			ExperimentInfos infos = new ExperimentInfos();
			infos.experimentStatus = ApplicationManager.getApplicationManager()
					.getNeptuneManager().getExperiment(expId).getState()
					.getValue();
			infos.topologyStatus = result.getStatus().toString();
			infos.topologyLog = result.getLog().prettyPrint(0);

			return infos;
		} catch (Exception e) {
			Debug.printTrace(e);
			throw new Exception(e);
		}
	}
}
